package com.yangcangclub.commons.annotation;

import com.alibaba.fastjson.JSON;
import com.yangcangclub.commons.result.MessageEnum;
import com.yangcangclub.commons.utils.StringUtil;
import com.yangcangclub.commons.config.SysConfig;
import com.yangcangclub.commons.result.ResultData;
import com.yangcangclub.commons.utils.HttpServletUtil;
import com.yangcangclub.commons.utils.NetUtil;
import com.yangcangclub.commons.utils.SignUtil;
import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;


/**
 * 通用拦截器 注意添加功能的性能问题，因为每次调用都会进入，所以耗时操作的慎用。
 */
public class SecurityInterceptor implements HandlerInterceptor {
    private static Logger logger = LoggerFactory.getLogger(SecurityInterceptor.class);
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    SysConfig sysConfig;
    @Autowired
    private SqlSessionTemplate masterSqlSessionTemplate;

    @Autowired
    UserAuth userAuth;

    /**
     * 整个请求处理完毕回调方法，即在视图渲染完毕时回调
     */
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception {

    }

    /**
     * 在业务处理器处理请求执行完成后 生成视图之前执行的动作
     */
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {

    }

    /**
     * 调用前执行 这个方法应该返回true， 允许DispatcherServlet继续处理请求。
     * 否则，DispatcherServlet会认为这个方法已经处理了请求，直接将响应返回给用户。
     */

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();

        if(logger.isDebugEnabled()){
            logger.info("header={},url={},params={}",new Object[]{request.getHeader("access_token"),request.getRequestURL(),JSON.toJSONString(request.getParameterMap())});
        }


        //数字签名
        RequestSign annotation2 = method.getAnnotation(RequestSign.class);
        if(annotation2!=null&& !SignUtil.signCheck(request)){
            ResultData check= new ResultData(MessageEnum.SIGN_ERROR);
            HttpServletUtil.WriteJSON(response, JSON.toJSONString(check));
            return false;
        }

        //接口权限
        ApolloSecurity annotation = method.getAnnotation(ApolloSecurity.class);
        if (annotation != null) {
            String accessToken= request.getHeader("access_token");
            String ip=null;
            if( StringUtil.isEmpty(accessToken)){
                accessToken= request.getParameter("access_token");
            }

         

            AuthEnum[] auth =annotation.auth();


            //1.权限判定
            ResultData check= new ResultData(MessageEnum.ACCESS_TOKEN_NOT_FOUND);
            boolean pubFlag=false;
            for(AuthEnum a:auth){
                if(a==AuthEnum.PUBLIC){
                    pubFlag=true;
                    break;
                }
            }

            //非公共访问权限
            if(!pubFlag){
                if(accessToken==null||accessToken.length()<32){
                    check=new ResultData(MessageEnum.ACCESS_TOKEN_ERROR);
                }else{

                    Map<String,Object> map = userAuth.getUserFullInfoByAccessToken(accessToken);

//                    //获取token 对应用户所拥有的权限
//                    Set <String> set= redisTemplate.opsForSet().members("user.access_token." + accessToken + ".privilege");
//                    if(set==null||set.size()==0){
//                        check=new ResultData(MessageEnum.ACCESS_TOKEN_NOT_FOUND);
//                    }else if( !redisTemplate.hasKey("user.access_token." + accessToken+".id")){
//                        check=new ResultData(MessageEnum.ACCESS_TOKEN_NOT_FOUND);
//                    }

                    if(null==map) {
                        check=new ResultData(MessageEnum.ACCESS_TOKEN_NOT_FOUND);
                    } else if(null==map.get("userInfo")||null==map.get("privilege")) {
                        check=new ResultData(MessageEnum.ACCESS_TOKEN_NOT_FOUND);
                    } else{
                        User user=(User)map.get("userInfo");
                        Set<String> set= (Set<String>)map.get("privilege");

                        //获取access_token对应的用户id
//                        Object obj= redisTemplate.boundValueOps("user.access_token." + accessToken+".id").get();
//                        int userId=(Integer)obj;
                        if(null==map.get("userInfo")){
                            check=new ResultData(MessageEnum.ACCESS_TOKEN_NOT_FOUND);
                        }else {
                            //比较权限是否一致
                            boolean flag=false;
                            for (AuthEnum a : auth) {
                                if (set.contains(""+a.getPower())) {
                                    check = new ResultData(MessageEnum.SUCCESS);
                                    flag=true;
                                    break;
                                }
                            }
//                            //如果权限还不足,则查库
//                            List<Integer> list= this.setUserTokenPrivilege(userId);
//                            for (AuthEnum a : auth) {
//                                if (list.contains(a.getPower())) {
//                                    check = new ResultData(MessageEnum.SUCCESS);
//                                    flag=true;
//                                    break;
//                                }
//                            }

                            if(!flag) {
                                logger.error("user privilege not enough userId={} ,privilege={}",user.getId(),JSON.toJSONString(auth));
                                check= new ResultData(MessageEnum.PERMISSION_DENIED);
                            }
                        }
                    }
                }

                //返回权限检查结果
                if(check.getCode()!=MessageEnum.SUCCESS.getCode()){
                    HttpServletUtil.WriteJSON(response, JSON.toJSONString(check));
                    return false;
                }
            }else{

                //ip
                ip= NetUtil.getIpAddress(request);
                if( !sysConfig.isTestEnv()&&StringUtil.isFullEmpty(ip)){
                    HttpServletUtil.WriteJSON(response,
                            JSON.toJSONString(new ResultData(MessageEnum.IP_INFO_ERROR)));
                    return false;
                }

            }




            //2.调用频次判定
            int seconds=annotation.time().getSeconds();
            int limits=annotation.limit();
            String uri=request.getRequestURI();
            //if(limits!=-1){
                if(limits==-100){//关闭
                String limitKey=null;
                if(StringUtil.isFullEmpty(accessToken)){
                    limitKey="user.ip."+ip+".uri."+uri+".limits";
                }else{
                    limitKey="user.access_token."+accessToken+".uri."+uri+".limits";
                }
                boolean hasKey= redisTemplate.hasKey(limitKey);

                if(hasKey){
                    BoundListOperations blo=redisTemplate.boundListOps(limitKey);
                    long  nowLimit= blo.size();
                    if(nowLimit<limits){
                        blo.leftPush(System.currentTimeMillis());
                    }else{
                        long earlyTime= (Long) blo.index(-1);

                        if((System.currentTimeMillis()-earlyTime)<=(seconds*1000)){//超过限制

                            HttpServletUtil.WriteJSON(response, JSON.toJSONString(new ResultData(MessageEnum.ACCESS_OUT_LIMITS)));
                            return false;
                        }else{
                            //放入新的，剔除旧的
                            blo.leftPush(System.currentTimeMillis());
                            blo.rightPop();
                        }
                    }

                    //刷新过期时间
                    blo.expire(24, TimeUnit.HOURS);

                }else{
                    redisTemplate.opsForList().leftPush(limitKey, System.currentTimeMillis());
                    redisTemplate.expire(limitKey,24, TimeUnit.HOURS);
                }
            }
        }
        // 放行
        return true;
    }
    
    
   
    /**
     * 设置用户id下所有token权限
     * 兼容傻逼处理机制---tian
     */
//    private List<Integer>   setUserTokenPrivilege(int userId){
//        Map<String,String> map= Maps.newHashMap();
//        map.put("user_id",""+userId);
//        map.put("status","N");
//        List<Integer> list= masterSqlSessionTemplate.selectList("user.UserMapper.selectUserPrivilege", map);
//        if(list==null){
//            list= Lists.newArrayList();
//        }
//
//        list.add(0);//至少拥有普通权限
//
//        final List<Integer> list2=list;
//        
//        //让之前的access_token全部失效
//        BoundSetOperations bso= redisTemplate.boundSetOps("user.id." + userId + ".access_tokens");
//        final Set<String> set= bso.members();
//
//
//        final RedisSerializer serializer = redisTemplate.getDefaultSerializer();
//        //redis管道批量处理
//        redisTemplate.executePipelined(new RedisCallback<Object>() {
//            @Override
//            public Object doInRedis(RedisConnection conn) throws DataAccessException {
//                byte[] key=null;
//                for(String token:set){//long类型
//                    key=serializer.serialize("user.access_token." + token + ".privilege");
//                    conn.del(key);
//                    for(Integer  p:list2){
//                        conn.sAdd(key, serializer.serialize(p+""));
//                    }
//                    conn.expire(key,sysConfig.getAccessTokenSeconds());
//                }
//                return null;
//            }
//        });
//        
//        return list;
//    }
}